home *** CD-ROM | disk | FTP | other *** search
-
- /* Copyright (C) 2009 Norman Solomon
- * e-mail: historytree.addon@yahoo.com
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the License.
- */
-
- // ==============================================================================
- // *** NOTES REGARDING INTEGRATION OF THIS OVERLAY WITH THE FIREFOX WINDOW
- // ------------------------------------------------------------------------------
- // One global variable and its associated class are declared in this overlay.
- // All other globals used by History Tree are declared in "historyViewer.js",
- // which is opened via window.open() as a self-contained, non-modal XUL window.
- // ==============================================================================
-
- // Global variable
- var historyTree;
-
- // ================================================================
- // Extension initialization - Called when Firefox window is opened
- // ================================================================
- window.addEventListener("load", historyTree_Initialize, false);
- function historyTree_Initialize()
- {
- // Create global historyTree object
- historyTree = new HistoryTree();
-
- // Initialize the extension
- historyTree.initialize();
- }
-
- // ==========================================================
- // Extension clean up - Called when Firefox window is closed
- // ==========================================================
- window.addEventListener("unload", historyTree_CleanUp, false);
- function historyTree_CleanUp()
- {
- // Destroy all listeners and free up memory used
- historyTree.cleanUp();
-
- // Clear global variable
- historyTree = null;
- }
-
- // ===========================================================================
- // HistoryTree class and its prototype *** SPANS THE REST OF THIS OVERLAY ***
- // ===========================================================================
- function HistoryTree() {}
-
- HistoryTree.prototype =
- {
- // Class wide variables
- cNodeList: new Array(), // List of HistoryNode objects for all FF Tabs
- cExtHistTreeWin: null, // "historyViewer.xul" (History Tree window)
- cTabLoadInterval: null, // setInterval() while FF Tab content is loading
- cExtLoadProcess: true, // Used for FF startup and "Stop Private Browsing"
- cHistEvent: "None", // FF sessionHistory event that occured
- cHistEventTabID: "", // FF sessionHistory event tabID
- cHistNodeAdded: false, // Set/used in sessHist "New" and pageLoaded()
- cFFContextMenuURL: "", // Detects "Open Link in New Tab" for a "# link"
- cOpenRecentlyClosed: false, // Used to restore "Recently Closed Tabs"
- cRecentlyClosedTabIDs: null, // Used to restore "Recently Closed Tabs"
- cStartPrivBrow: false, // Used to clear history if "Start Private Browsing"
-
- // =====================================
- // HistoryTree extension initialization
- // =====================================
- initialize: function()
- {
- // Try to initialize the extension
- try
- {
- // Put FF start date/time in App storage (for display in ext window)
- var startDate = Application.storage.get("FFstartDate", null);
- if (startDate === null)
- Application.storage.set("FFstartDate", new Date());
-
- // Add page-load, tab and menu click FF event listeners
- historyTree.addOrRemoveEventListeners(true);
-
- // Set class flag that causes function exits while Tabs load
- historyTree.cExtLoadProcess = true;
-
- // Start setInterval() that runs until all Tabs are loaded
- historyTree.cTabLoadInterval = setInterval
- (historyTree.createSessHistWhenTabsFinishLoading, 800);
- }
- catch (e)
- {
- alert("History Tree Extension - Overlay Initialization Error");
- }
- },
-
- // ================================
- // HistoryTree extension clean-up
- // ================================
- cleanUp: function()
- {
- // Close the "historyViewer.xul" window if shows this FF window history
- // *** NOTE - cExtHistTreeWin may have been set in "historyViewer.js"
- if (historyTree.cExtHistTreeWin)
- {
- if (!historyTree.cExtHistTreeWin.closed)
- {
- if (historyTree.cExtHistTreeWin.gParentFFWin === window)
- historyTree.cExtHistTreeWin.close();
- }
- }
-
- // Remove page-load, tab and menu click FF event listeners
- historyTree.addOrRemoveEventListeners(false);
-
- // Remove window load and unload listeners
- window.removeEventListener("load", historyTree_Initialize, false);
- window.removeEventListener("unload", historyTree_CleanUp, false);
- },
-
- // ==========================================================
- // Adds or removes FF event listeners used by the extension
- // ==========================================================
- addOrRemoveEventListeners: function(adding)
- {
- // Page-load and gBrowser sessionHistory listeners
- const NOTIFY_DOC = Components.interfaces.nsIWebProgress.NOTIFY_STATE_DOCUMENT;
- if (adding)
- {
- window.getBrowser().addProgressListener(historyTree.loadListener, NOTIFY_DOC);
- gBrowser.sessionHistory.addSHistoryListener(historyTree.myHist);
- }
- else
- {
- window.getBrowser().removeProgressListener(historyTree.loadListener, NOTIFY_DOC);
- gBrowser.sessionHistory.removeSHistoryListener(historyTree.myHist);
- }
-
- // Tab open, Tab close and tab selected listeners
- var container = gBrowser.tabContainer;
- if (container)
- {
- if (adding)
- {
- container.addEventListener("TabOpen", historyTree.tabAdded, false);
- container.addEventListener("TabSelect", historyTree.tabSelected, false);
- container.addEventListener("TabClose", historyTree.tabClosed, false);
- }
- else
- {
- container.removeEventListener("TabOpen", historyTree.tabAdded, false);
- container.removeEventListener("TabSelect", historyTree.tabSelected, false);
- container.removeEventListener("TabClose", historyTree.tabClosed, false);
- }
- }
-
- // --------------------------------------------------------------------
- // FF window menu-bar "History" > "Recently Closed Tabs" > popupMenu
- var FFHistUndoMenu = document.getElementById("historyUndoPopup");
- if (FFHistUndoMenu)
- {
- if (adding)
- FFHistUndoMenu.addEventListener("click", historyTree.getFFHistUndoClick, false);
- else
- FFHistUndoMenu.removeEventListener("click", historyTree.getFFHistUndoClick, false);
- }
-
- // FF tab-header-bar context menu (appears via mouse right-click)
- // *** NOTE - "anonid"'s are listed in FF 3.5 (3.0.x ?!) "tabBrowser.xml"
- var FFTabContextMenu = document.getAnonymousElementByAttribute
- (gBrowser, "anonid", "tabContextMenu");
- if (FFTabContextMenu)
- {
- if (adding)
- FFTabContextMenu.addEventListener("click", historyTree.getFFTabContextMenuClick, false);
- else
- FFTabContextMenu.removeEventListener("click", historyTree.getFFTabContextMenuClick, false);
- }
-
- // FF window menu-bar "Tools" menu - Used for "Start/Stop Privare Browsing"
- var FFHistUndoMenu = document.getElementById("menu_ToolsPopup");
- if (FFHistUndoMenu)
- {
- if (adding)
- FFHistUndoMenu.addEventListener("click", historyTree.getFFToolsMenuClick, false);
- else
- FFHistUndoMenu.removeEventListener("click", historyTree.getFFToolsMenuClick, false);
- }
-
- // FF content-area context menu (appears via mouse right-click)
- var FFcontextMenu = document.getElementById("contentAreaContextMenu");
- if (FFcontextMenu)
- {
- if (adding)
- FFcontextMenu.addEventListener("click", historyTree.getFFcontextMenuURL, false);
- else
- FFcontextMenu.removeEventListener("click", historyTree.getFFcontextMenuURL, false);
- }
- },
-
- // ==================================================
- // Fires when a new Tab is added to this FF window
- // ==================================================
- tabAdded: function(event)
- {
- // Add tabID for re-opened "Recently Closed Tab" if req
- if (historyTree.cOpenRecentlyClosed)
- historyTree.cRecentlyClosedTabIDs.push(event.target.linkedPanel);
-
- // Add a new sessionHistory Listener to this new Tab
- var browser = event.target.linkedBrowser;
- browser.sessionHistory.addSHistoryListener(historyTree.myHist);
- },
-
- // ========================================
- // Fires when an FF window Tab is selected
- // ========================================
- tabSelected: function(event)
- {
- // Exit if FF is still loading
- if (historyTree.cExtLoadProcess)
- return;
-
- // Update cNodeList[] entries if selected Tab is not loading
- // *** NOTE - Updating while Tab is loading must be avoided
- // because of multi-part page download sibling addition errors
- var browser = event.target.linkedBrowser;
- var tabID = event.target.linkedPanel;
- if (!browser.webProgress.isLoadingDocument)
- historyTree.updateSessionHistoryForTab(tabID);
- },
-
- // =======================================
- // Fires when an FF window Tab is closed
- // =======================================
- tabClosed: function(event)
- {
- // Exit if FF is still loading
- if (historyTree.cExtLoadProcess)
- return;
-
- // Update the cNodeList[] sessHist entries for this Tab
- var tabID = event.target.linkedPanel;
- historyTree.updateSessionHistoryForTab(tabID);
-
- // Store sessHist URIs in last cNodeList[] entry for closed Tab
- var browser = event.target.linkedBrowser;
- historyTree.saveClosedTabURIList(browser.sessionHistory, tabID);
-
- // Remove the closed Tab's sessionHistoryListener
- browser.sessionHistory.removeSHistoryListener(historyTree.myHist);
- },
-
- // =======================================================================
- // Fires when user clicks on any FF "Recently Closed Tabs" popupMenu item
- // *** NOTE - popupMenu is populated dynamically, so items have no id's
- // =======================================================================
- getFFHistUndoClick: function(event)
- {
- // *** NOTE - Can get menu item labels for UI spoken lang like this;
- // gNavigatorBundle.getString("menuOpenAllInTabs.label");
-
- // Set class vars used to restore "Recently Closed Tabs"
- historyTree.cOpenRecentlyClosed = true;
- historyTree.cRecentlyClosedTabIDs = new Array();
- },
-
- // ==================================================================
- // Fires when user clicks on any FF tab-header-bar context menu item
- // ==================================================================
- getFFTabContextMenuClick: function(event)
- {
- // Check for "Undo Close Tab" menu-item click
- if (event.target.id === "context_undoCloseTab")
- {
- // Set class vars used to restore "Recently Closed Tabs"
- historyTree.cOpenRecentlyClosed = true;
- historyTree.cRecentlyClosedTabIDs = new Array();
- }
- },
-
- // ===================================================================
- // Fires when user clicks on any FF window menu-bar "Tools" menu item
- // ===================================================================
- getFFToolsMenuClick: function(event)
- {
- // Check for "Start/Stop Private Browsing" menu-item click
- if (event.target.id === "privateBrowsingItem")
- {
- // Set class flag that causes function exits while Tabs load
- historyTree.cExtLoadProcess = true;
-
- // Set class flag if FF is user clicked on "Start Private Browsing"
- var inPrivBrow = Components.classes["@mozilla.org/privatebrowsing;1"].
- getService(Components.interfaces.nsIPrivateBrowsingService).
- privateBrowsingEnabled;
-
- if (!inPrivBrow)
- historyTree.cStartPrivBrow = true;
-
- // Start setInterval() that runs until all Tabs are loaded
- historyTree.cTabLoadInterval = setInterval
- (historyTree.createSessHistWhenTabsFinishLoading, 800);
- }
- },
-
- // ===================================================================
- // Fires when user clicks on any FF content-area context menu item
- // ===================================================================
- getFFcontextMenuURL: function(event)
- {
- // If user clicked on FF gContextMenu item "Open Link in New Tab"
- // store the link URL in class var (for use in sessHistory Listener)
- if (event.target.id === "context-openlinkintab" && gContextMenu.onLink)
- historyTree.cFFContextMenuURL = gContextMenu.linkURL;
- else
- historyTree.cFFContextMenuURL = "";
- },
-
- // =====================================================================
- // A new myHist Listener is added to every Tab opened in this FF window
- // =====================================================================
- myHist:
- {
- OnHistoryGoBack : function(aURI) {
- // Set class vars used in pageLoaded()
- historyTree.cHistEvent = "Back";
- historyTree.cHistEventTabID = gBrowser.tabContainer.selectedItem.linkedPanel;
- return true;
- },
-
- OnHistoryGoForward : function(aURI) {
- // Set class vars used in pageLoaded()
- historyTree.cHistEvent = "Forward";
- historyTree.cHistEventTabID = gBrowser.tabContainer.selectedItem.linkedPanel;
- return true;
- },
-
- OnHistoryGotoIndex : function(aIndex, aURI) {
- // Set class vars used in pageLoaded()
- historyTree.cHistEvent = "Goto";
- historyTree.cHistEventTabID = gBrowser.tabContainer.selectedItem.linkedPanel;
- return true;
- },
-
- OnHistoryReload : function(aURI, aFlags) {
- // Set class vars used in pageLoaded()
- historyTree.cHistEvent = "Reload";
- historyTree.cHistEventTabID = gBrowser.tabContainer.selectedItem.linkedPanel;
- return true;
- },
-
- OnHistoryNewEntry : function(aURI) {
- // Exit if FF just started up, or if user is opening "Recently Closed Tabs"
- if (historyTree.cExtLoadProcess || historyTree.cOpenRecentlyClosed)
- return true;
-
- // --------------------------------------------------------------------------
- // The aURI item has NOT been added to FF sessionHistory list at this point,
- // and the variable length, cascading, page-load sequence has NOT started
- // --------------------------------------------------------------------------
- // Synchronise the current cNodeList[] sessHist chain for this Tab
- var tabID = gBrowser.tabContainer.selectedItem.linkedPanel;
- historyTree.synchSessHistChainForTab(tabID);
-
- // -----------------------------------------------------------------------------
- // Add the cNodeList[] item here if function pageLoaded() will not be called.
- // This occurs for a "# link" when the page containing the "# link" is showing,
- // and the user is NOT doing an "Open Link in New Tab" for that "# link"
- // -----------------------------------------------------------------------------
- var sessHist = gBrowser.sessionHistory;
- if (sessHist.count > 0 && aURI.spec !== historyTree.cFFContextMenuURL)
- {
- // Reset class var used to detect "Open # Link in New Tab"
- historyTree.cFFContextMenuURL = "";
-
- // Check for a "# link" in-page-jump (FF has already scrolled the page)
- var pos = aURI.spec.indexOf("#");
- if (pos !== -1)
- {
- // Add cNodeList[] entry if current page is "# link" containing page
- var thisPath = aURI.spec.substr(0, pos);
- var prevNdx = sessHist.index;
- var prevItem = sessHist.getEntryAtIndex(prevNdx, false);
- var prevPath = prevItem.URI.spec.substr(0, pos);
-
- if (thisPath === prevPath)
- {
- historyTree.addInPageLinkHistoryNode(aURI.spec, prevNdx + 1);
- return true;
- }
- }
- }
-
- // ---------------------------------------------------------------
- // Reset class var used to detect "Open # Link in New Tab"
- historyTree.cFFContextMenuURL = "";
-
- // Set class vars used in pageLoaded()
- historyTree.cHistNodeAdded = false;
- historyTree.cHistEvent = "New";
- historyTree.cHistEventTabID = tabID;
-
- // The variable length, cascading, page-load sequence begins next
- return true;
- },
-
- OnHistoryPurge : function(aParam) {
- // **** NOTHING IS DONE FOR THIS EVENT ****
- historyTree.cHistEvent = "Purge";
- historyTree.cHistEventTabID = gBrowser.tabContainer.selectedItem.linkedPanel;
- return true;
- },
-
- QueryInterface: function(aIID) {
- if (aIID.equals(Components.interfaces.nsISHistoryListener) ||
- aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
- aIID.equals(Components.interfaces.nsISupports))
- {
- return this;
- }
- throw Components.Exception(Components.results.NS_ERROR_NO_INTERFACE);
- },
-
- GetWeakReference: function() {
- return Components.classes["@mozilla.org/appshell/appShellService; 1"]
- .createInstance(Components.interfaces.nsIWeakReference);
- }
- },
-
-
- // ========================================================================
- // These events fire when web-page SEGMENTS are loaded into Firefox - BUT
- // there seems to be no way to detect when the FINAL segment will be loaded
- // ========================================================================
- loadListener:
- {
- QueryInterface: function(aIID)
- {
- if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
- aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
- aIID.equals(Components.interfaces.nsISupports))
- return this;
- throw Components.results.NS_NOINTERFACE;
- },
-
- onStateChange: function(aWebProgress, aRequest, aFlag, aStatus)
- {
- // If you use myListener for more than one tab/window, use
- // aWebProgress.DOMWindow to obtain the tab/window which triggers the state change
- if (aFlag & Components.interfaces.nsIWebProgressListener.STATE_STOP
- && aFlag & Components.interfaces.nsIWebProgressListener.STATE_IS_WINDOW)
- {
- // This event repeats for some web-pages - Such as e-mail sites etc
- // In these cases the last element in the nodeList() array is replaced
- historyTree.pageLoaded();
- }
- return 0;
- },
-
- onLocationChange: function(aProgress, aRequest, aURI)
- {
- // This fires when the location bar changes; i.e load event confirmed
- return 0;
- },
-
- // For definitions of the remaining functions see XULPlanet.com
- onProgressChange: function() {return 0;},
- onStatusChange: function() {return 0;},
- onSecurityChange: function() {return 0;},
- onLinkIconAvailable: function() {return 0;}
- },
-
-
- // ***********************************************************************
- // ***** *****
- // ***** UTILITY FUNCTIONS THAT BUILD historyTree.cNodeList[] *****
- // ***** WHICH STORES THE SESSION HISTORY FOR THIS FF WINDOW *****
- // ***** *****
- // ***********************************************************************
-
- // =============================================================
- // Returns a HistoryNode for storage in historyTree.cNodeList[]
- // =============================================================
- histNode: function(tab, uri)
- {
- // Define the HistoryNode object
- var HistoryNode = function (tab, uri)
- {
- this.tab = tab; // FF tabID of Tab containing FF sessHist
- this.sessHistNdx; // FF sessHist.index
- this.uri = uri; // FF sessHist[index].URI.spec
- this.uCaseUri; // Used to speed up search
- this.desc; // FF sessHist[index].title (web-page desc)
- this.uCaseDesc; // Used to speed up ext win searches
- this.timeInfo; // Time web-page was first loaded into tab
-
- this.imgData; // Data version of web-page ".jpeg" image
- this.imgToDraw; // ".jpeg" drawn in extension main window
- this.imgCreated; // Set = true when ".jpeg" is drawn
- this.treeNodeNdx; // Only used in extension main window
-
- // Array property - Used when restoring "Recently Closed Tabs"
- this.closedTabURIs = null;
- };
-
- // Create and return a HistoryNode object
- var node = new HistoryNode(tab, uri);
- return node;
- },
-
- // =============================================================
- // Opens extension main window - Which shows the sessionHistory
- // Called from two oncommand() events in "firefoxOverlay.xul"
- // =============================================================
- showSessionHistoryExtensionWindow: function()
- {
- // Close the extension window if its open already
- if (historyTree.cExtHistTreeWin)
- {
- if (!historyTree.cExtHistTreeWin.closed)
- historyTree.cExtHistTreeWin.close();
- }
-
- // Open or re-open window - which refreshes its data
- var winPath = "chrome://historyTree/content/historyViewer.xul";
- var chromeFeatures = "chrome,centerscreen,resizable=yes";
- historyTree.cExtHistTreeWin = window.open(winPath,"historyViewer",chromeFeatures);
- },
-
- // ====================================================================
- // Returns dataURL for the web-page image in the Tab with passed tabID
- // ====================================================================
- getWebPageImageData: function(tabID)
- {
- try
- {
- var browser; // FF Tab/Browser
- var pageWin; // FF contentWindow
- var ndx, w, h; // Integers
- var canvas; // <canvas...>
- var ctx; // <canvas...> graphics context
-
- // -------------------------------------------------
- // Get tab/browser for passed tabID
- browser = null;
- ndx = 0;
- while (browser === null && ndx < gBrowser.browsers.length)
- {
- if (tabID === gBrowser.mTabs[ndx].linkedPanel)
- browser = gBrowser.getBrowserAtIndex(ndx);
- else
- ndx ++;
- }
-
- // Get width and height of tab/browser contentWindow
- pageWin = browser.contentWindow;
- w = pageWin.innerWidth + pageWin.scrollMaxX;
- if (pageWin.scrollMaxY > 0) // Vertical scrollbars present
- w -= 16;
-
- // h/w ratio MUST be 0.65 or displayed ".jpeg"'s will be distorted
- h = w * 0.65;
- if (pageWin.scrollMaxX > 0) // Horizontal scrollbars present
- h -= 16;
-
- // Set width and height of canvas that obtains web-page screenshot
- // This canvas is hidden in the right hand side of the FF statusbar
- canvas = document.getElementById("testCanvas");
- canvas.style.width = w + "px";
- canvas.style.height = h + "px";
- canvas.width = w;
- canvas.height = h;
-
- // Draw web-page screenshot on canvas using a white background
- // Note that (x,y) can be used to get HIDDEN part of a page
- ctx = canvas.getContext('2d');
- ctx.clearRect(0, 0, w, h);
- ctx.drawWindow(pageWin, pageWin.scrollX, pageWin.scrollY, w, h, "white");
-
- // Return a data string representation of the contentWindow
- // "png" images are SLIGHTLY less blurry, but need more memory
- return canvas.toDataURL("image/jpeg", "");
- }
- catch (e)
- {
- // A blank rectangle will be drawn in extension window
- return null;
- }
- },
-
- // ==============================================================================
- // Adds to, or edits, the HistoryNode's in cNodeList[], depending on FF events
- // ------------------------------------------------------------------------------
- // ONLY called from nsiWebProgressListener, when page-segment load event fires
- // HOWEVER, there is no way to detect when the FINAL page-segment will be loaded
- // ==============================================================================
- pageLoaded: function()
- {
- var tabID; // FF Tab identifier
- var sessHist; // FF sessHist
- var node; // HistoryNode
-
- // ----------------------------------------------------------
- // Exit if FF Tab page load setInterval() is still running
- if (historyTree.cExtLoadProcess)
- {
- return;
- }
- else if (historyTree.cOpenRecentlyClosed)
- {
- // User clicked on a "Recently Closed Tabs" FF sub-menu item
- historyTree.restoreRecentlyClosedTabs(historyTree.cRecentlyClosedTabIDs);
-
- // Reset class flags
- historyTree.cOpenRecentlyClosed = false;
- historyTree.cRecentlyClosedTabIDs = null;
- }
- else
- {
- // User is opening a new page or navigating to an open page via FF sessHist
- tabID = gBrowser.tabContainer.selectedItem.linkedPanel;
-
- // Ensure user is still on the Tab that initiated the sessHist Listener event
- if (tabID === historyTree.cHistEventTabID)
- {
- // Add to or adjust cNodeList[] depending on FF sessHist event
- if (historyTree.cHistEvent === "New" && !historyTree.cHistNodeAdded)
- {
- // Add a HistoryNode to the end of cNodeList[]
- historyTree.updateSessionHistoryForTab(tabID);
- historyTree.cHistNodeAdded = true;
- }
- else
- {
- // Update cNodeList[] entry props to match sessHist item
- sessHist = gBrowser.sessionHistory;
- node = historyTree.nodeFromSessHistNdx(sessHist.index, tabID);
- if (node !== null)
- historyTree.makeHistoryNodeMatchFFsessHist(tabID, node, sessHist);
- }
- }
- }
- },
-
- // ===================================================
- // Fills cNodeList[] when all FF Tabs finish loading
- // Called via setInterval() when FF window opened
- // ===================================================
- createSessHistWhenTabsFinishLoading: function()
- {
- // Check if all FF Tabs have finished loading
- if (historyTree.allTabsLoaded())
- {
- // Stop setInterval() and set class flag
- clearInterval(historyTree.cTabLoadInterval);
- historyTree.cTabLoadInterval = null;
-
- // Close extension win if "Private Browsing" started
- if (historyTree.cStartPrivBrow)
- {
- // Reset class flag
- historyTree.cStartPrivBrow = false;
-
- // Find out if FF is in "Private Browsing" mode
- var inPrivBrow = Components.classes["@mozilla.org/privatebrowsing;1"].
- getService(Components.interfaces.nsIPrivateBrowsingService).
- privateBrowsingEnabled;
-
- if (!inPrivBrow)
- {
- // Private browsing was cancelled so do nothing
- historyTree.cExtLoadProcess = false;
- return;
- }
- else if (historyTree.cExtHistTreeWin && !historyTree.cExtHistTreeWin.closed)
- {
- // Close extension window at start of private browsing
- historyTree.cExtHistTreeWin.close();
- }
- }
-
- // Clear out class array
- historyTree.cNodeList = new Array();
-
- // Fill cNodeList[] and add sessHist Listeners
- var tabID;
- var brow;
-
- for (var i = 0; i < gBrowser.browsers.length; i++)
- {
- // Add cNodeList[] sessHist entries for this Tab
- tabID = gBrowser.mTabs[i].linkedPanel;
- historyTree.synchSessHistChainForTab(tabID);
-
- // Add Tab hist Listener (addition via tabAdded() sometimes fails)
- brow = gBrowser.getBrowserAtIndex(i);
- brow.sessionHistory.addSHistoryListener(historyTree.myHist);
- }
-
- // Reset class flag
- historyTree.cExtLoadProcess = false;
- }
- },
-
- // ===============================================
- // Adds a HistoryNode to the end of cNodeList[]
- // ===============================================
- addHistoryNode: function(tabID, sessHist, sessHistNdx)
- {
- var histItem; // FF sessHist item
- var histURI; // FF sessHist item URI
- var node; // HistoryNode
- var pageDesc; // String
- var timeNow; // JS date()
-
- // ------------------------------------------------------
- // Get information describing matching FF sessHist entry
- histItem = sessHist.getEntryAtIndex(sessHistNdx, false);
- histURI = histItem.URI.spec;
-
- // Create a HistoryNode, passing in .tab and .uri
- node = historyTree.histNode(tabID, histURI);
-
- // Set node FF sessHist.index and image first-time draw flag
- node.sessHistNdx = sessHistNdx;
- node.imgCreated = false;
-
- // Set node web-page description, including any "#" suffix
- pageDesc = historyTree.pageDescFor(histItem.title, histURI);
- node.desc = pageDesc;
-
- // Set upper case URI and desc - Used to speed up ext win search
- node.uCaseUri = histURI.toUpperCase();
- node.uCaseDesc = pageDesc.toUpperCase();
-
- // Set time page loaded - Shown as "16.42" etc. in ext window
- timeNow = new Date().toTimeString();
- node.timeInfo = timeNow.substr(0,5);
-
- // Get and set web-page image if page currently visible in Tab
- if (sessHistNdx === sessHist.index)
- {
- // Get node web-page image dataURL for later ".jpeg" display
- node.imgData = historyTree.getWebPageImageData(tabID);
- }
- else
- {
- // Web-page image will be drawn as a white rectangle
- node.imgData = null;
- }
-
- // Add the HistoryNode to the end of cNodeList[]
- historyTree.cNodeList.push(node);
- },
-
- // =====================================================================
- // Adds a "# link" HistoryNode to cNodeList[] - ONLY Called from myHist
- // "New" event. *** NOTE - Will add an out of sequence cNodeList[] entry
- // if user clicks on the "# link" BEFORE the containing page has loaded.
- // This FF asynch error is fixed in function synchSessHistChainForTab()
- // =====================================================================
- addInPageLinkHistoryNode: function(URI, sessHistNdx)
- {
- // Add a "# link" HistoryNode to the end of cNodeList[]
- var tabID = gBrowser.tabContainer.selectedItem.linkedPanel;
- var node = historyTree.histNode(tabID, URI);
-
- // Set HistoryNode properties
- node.uCaseUri = URI.toUpperCase();
- node.sessHistNdx = sessHistNdx;
- var timeNow = new Date().toTimeString();
- node.timeInfo = timeNow.substr(0,5);
-
- var pageDesc = historyTree.pageDescFor(gBrowser.contentTitle, URI);
- node.desc = pageDesc;
- node.uCaseDesc = pageDesc.toUpperCase();
- node.imgData = historyTree.getWebPageImageData(tabID);
- node.imgCreated = false;
-
- // Add HistoryNode to end of cNodeList[]
- historyTree.cNodeList.push(node);
- },
-
- // ======================================================
- // Updates cNodeList[] entry to match FF sessHist item
- // ======================================================
- makeHistoryNodeMatchFFsessHist: function(tabID, node, sessHist)
- {
- // Update page image if page is loaded/visible in passed tabID
- if (node.sessHistNdx === sessHist.index)
- {
- // Set dataURL and flag for ".jpeg" image display
- node.imgData = historyTree.getWebPageImageData(tabID);
- node.imgCreated = false;
- }
-
- // Update cNodeList[] HistoryNode URI
- var histItem = sessHist.getEntryAtIndex(sessHist.index, false);
- var uriSpec = histItem.URI.spec;
- node.uri = uriSpec;
- node.uCaseUri = uriSpec.toUpperCase();
-
- // Update cNodeList[] HistoryNode description
- var pageDesc = historyTree.pageDescFor(histItem.title, node.uri);
- node.desc = pageDesc;
- node.uCaseDesc = pageDesc.toUpperCase();
- },
-
- // ===============================================================
- // Updates current cNodeList[] entry for Tab with passed tabID
- // Called when user clicks on a Tab or opens extension window etc
- // ===============================================================
- updateSessionHistoryForTab: function(tabID)
- {
- var sessHist; // FF sessHist
- var histItem; // FF sessHist item
- var lastHistNdx, myLastNdx; // Integers
- var node; // HistoryNode
- var newEntry = ""; // String
-
- // ----------------------------------------------------------
- // Only process FF sessionHistory's that have some entries
- sessHist = historyTree.sessHistFromTabID(tabID);
- if (sessHist !== null)
- {
- // *** NOTE - sessHist.count will === 0 if Tab has no content
- // FF 3.0.x menu option "File > New Tab" creates this situation
- // If a Tab does have only one blank page then FF 3.0.x
- // creates a URI === "about:blank", but FF 3.5.x does not?!
- if (sessHist.count > 0)
- {
- // Decide whether to add to or update cNodeList[]
- myLastNdx = historyTree.sessHistLengthForTab(tabID);
- lastHistNdx = sessHist.count - 1;
- histItem = sessHist.getEntryAtIndex(lastHistNdx, false);
- node = historyTree.nodeFromSessHistNdx(lastHistNdx, tabID);
-
- // Can't check node.uri if node is null - so set flag
- // (could use constants, but this is readable and clear)
- if (lastHistNdx > myLastNdx || node === null)
- newEntry = "child";
- else if (node.uri !== histItem.URI.spec || myLastNdx > lastHistNdx)
- newEntry = "sibling";
- else
- newEntry = "none";
-
- // If adding a tab root sibling delete the entire tab sub-tree first
- // This is sometimes needed after "Tools > Clear Recent History"
- // There is no logical alternative, since doing nothing causes errors
- if (newEntry === "sibling" && lastHistNdx === 0)
- {
- historyTree.deleteAllNodesForTab(tabID);
- historyTree.addHistoryNode(tabID, sessHist, lastHistNdx);
- return;
- }
-
- // Add to or update cNodeList[] sessHist entries for passed tabID
- if (newEntry === "child" || newEntry === "sibling")
- {
- // The last cNodeList[] child/sibling entry is missing
- historyTree.addHistoryNode(tabID, sessHist, lastHistNdx);
- }
- else
- {
- // All cNodeList[] entries have been added for this Tab
- node = historyTree.nodeFromSessHistNdx(sessHist.index, tabID);
- if (node !== null)
- {
- // Update HistoryNode to match FF sessHist entry
- historyTree.makeHistoryNodeMatchFFsessHist(tabID, node, sessHist);
- }
- }
- }
- }
- },
-
- // ================================================================
- // Updates cNodeList[] sessHist chain for passed tabID. Called;
- // ----------------------------------------------------------------
- // 1) When FF window opened and from sessHist listener "New" event
- // 2) For all FF Tabs, just before opening extension main window
- // ================================================================
- synchSessHistChainForTab: function(tabID)
- {
- var tabNodes = new Array(); // Temp array
- var node, root, thisNode, nextNode; // HistoryNode's
- var tabLen, lastHistNdx, myLastNdx; // Integers
- var sessHist; // FF sessHist
- var histItem; // FF sessHist item
-
- // ---------------------------------------------------------
- // Create cNodeList[] HistoryNode ref array for passed tabID
- for (var ndx = 0; ndx < historyTree.cNodeList.length; ndx++)
- {
- node = historyTree.cNodeList[ndx];
- if (node.tab === tabID)
- tabNodes.push(node);
- }
-
- // Make sure root node for passed tabID has a zero sessHistNdx
- if (tabNodes.length > 0)
- {
- root = tabNodes[0];
- if (root.sessHistNdx !== 0)
- root.sessHistNdx = 0;
- }
-
- // Make sure cNodeList[] chains are incrementally sequential
- // i.e. correct any invalid sessHist chain sequences for tabID
- tabLen = tabNodes.length - 1;
- for (var ndx = 0; ndx < tabLen; ndx++)
- {
- // Reset chain sequence if its not incrementally sequential
- thisNode = tabNodes[ndx];
- nextNode = tabNodes[ndx + 1];
- if (nextNode.sessHistNdx > thisNode.sessHistNdx + 1)
- nextNode.sessHistNdx = thisNode.sessHistNdx + 1;
- }
-
- // Remove all elements from tabID reference array
- tabNodes.splice(0);
-
- // ----------------------------------------------------------------
- // Synchronise chain in cNodeList[] with current FF sessHist chain
- // i.e. correct any asynchronous FF page-load errors for this Tab
- sessHist = historyTree.sessHistFromTabID(tabID);
- if (sessHist !== null)
- {
- if (sessHist.count > 0)
- {
- // Check if any cNodeList[] entries are missing from end of chain
- myLastNdx = historyTree.sessHistLengthForTab(tabID);
- lastHistNdx = sessHist.count - 1;
- if (lastHistNdx > myLastNdx)
- {
- // Add all missing cNodeList[] entries to end of current chain
- for (var ndx = myLastNdx + 1; ndx < sessHist.count; ndx ++)
- historyTree.addHistoryNode(tabID, sessHist, ndx);
- }
-
- // Correct any mismatched cNodeList[] HistoryNode's in current chain
- for (var ndx = 0; ndx < sessHist.count; ndx ++)
- {
- node = historyTree.nodeFromSessHistNdx(ndx, tabID);
- if (node !== null)
- {
- histItem = sessHist.getEntryAtIndex(ndx, false);
- if (node.uri !== histItem.URI.spec)
- {
- // Update HistoryNode for current FF sessHist entry
- historyTree.makeHistoryNodeMatchFFsessHist(tabID, node, sessHist);
- }
- }
- }
- }
- }
- },
-
- // ==========================================================
- // Stores sessHist URI list in cNodeList[] entry for tabID
- // Used when user restores Tabs via "Recently Closed Tabs"
- // *** ONLY called when closing a Tab via tabClosed()
- // ==========================================================
- saveClosedTabURIList: function(sessHist, tabID)
- {
- var histItem; // FF sessHist item
- var node; // HistoryNode
- var lastNdx; // Integer
- var sessHistURIs = new Array();
-
- // --------------------------------------------
- // Store sessionHistory URI list in an array
- for (var i = 0; i < sessHist.count; i++)
- {
- histItem = sessHist.getEntryAtIndex(i, false);
- sessHistURIs.push(histItem.URI.spec);
- }
-
- // Attach array to LAST cNodeList[] entry for passed tabID
- // (this array prop will be set = null if Tab is restored)
- lastNdx = historyTree.cNodeList.length - 1;
- for (var i = lastNdx; i >= 0; i--)
- {
- node = historyTree.cNodeList[i];
- if (node.tab === tabID)
- {
- node.closedTabURIs = sessHistURIs;
- return;
- }
- }
-
- // Remove all elements from temp array
- sessHistURIs.splice(0);
- },
-
- // =================================================================
- // Restores "Recently Closed Tabs" when user chooses this FF option
- // Called from pageLoaded() - Which is final Tab restoration event
- // =================================================================
- restoreRecentlyClosedTabs: function(restoredTabIDs)
- {
- var tabID, oldTabID; // FF Tab identifiers
- var sessHist; // FF sessHist
- var histItem; // FF sessHist item
- var histURI, savedURI; // FF sessHist item URI
- var node; // HistoryNode
- var matchFound = false; // Boolean
- var ndx, row, pos; // Integers
-
- // --------------------------------------------------
- // Restore all "Recently Closed Tabs" in cNodeList[]
- for (var i = 0; i < restoredTabIDs.length; i++)
- {
- // Delete any nodes user added while FF was doing Tab restoration
- tabID = restoredTabIDs[i];
- historyTree.deleteAllNodesForTab(tabID);
-
- // Get FF sessionHistory for restored tabID
- sessHist = historyTree.sessHistFromTabID(tabID);
-
- // Only process FF sessHist URI lists that have some entries
- if (sessHist !== null)
- {
- if (sessHist.count > 0)
- {
- // Search BACKWARDS for a matching cNodeList[] saved sessHist
- // (this will have been saved when the Tab was closed)
- matchFound = false;
- ndx = historyTree.cNodeList.length - 1;
- while (!matchFound && ndx >= 0)
- {
- // Make sure saved sessHist is not null and same length
- node = historyTree.cNodeList[ndx];
- ndx --;
- if (node.closedTabURIs !== null)
- {
- if (node.closedTabURIs.length === sessHist.count)
- {
- // Check if saved sessHist URI's match FF sessHist URI's
- matchFound = true;
- row = 0;
- while (matchFound && row < sessHist.count)
- {
- // Get pair of URI's for comparison
- histItem = sessHist.getEntryAtIndex(row, false);
- histURI = histItem.URI.spec;
- savedURI = node.closedTabURIs[row];
-
- // URI lists don't match if this URI pair don't match
- if (histURI !== savedURI)
- {
- matchFound = false;
-
- // *** FF BUG CODE AROUND *** i.e. FF can restore
- // the wrong last page if that page is a "# link"
- if (row === sessHist.count - 1)
- {
- pos = histURI.indexOf("#");
- if (pos !== -1)
- {
- if (histURI.substr(0, pos)
- === savedURI.substr(0, pos))
- matchFound = true;
- }
- }
- }
- // Move to next pair of sessHist URI items
- row ++;
- }
- }
- }
- }
-
- // Restore closed Tab if a matching cNodeList[] sessHist was found
- if (matchFound)
- {
- // Match found - So reset tabID's in matching cNodeList[] entries
- node.closedTabURIs = null; // Stops re-use
- oldTabID = node.tab;
- for (var ndx = 0; ndx < historyTree.cNodeList.length; ndx++)
- {
- node = historyTree.cNodeList[ndx];
- if (node.tab === oldTabID)
- node.tab = tabID;
- }
- }
- else
- {
- // No match found - Could occur if FF startup data included
- // some "Recently Closed Tabs" from previous "Save and Quit"
- // So add a chain of HistoryNode's to cNodeList[]
- for (var ndx = 0; ndx < sessHist.count; ndx++)
- {
- // Add HistoryNode to the end of cNodeList[]
- historyTree.addHistoryNode(tabID, sessHist, ndx);
- }
- }
- }
- }
- }
- },
-
- // ==============================================================
- // Deletes all cNodeList[] HistoryNodes that have passed tabID
- // Called just before restoring a "Recently Closed Tab" and if
- // a tab root sibling is added in updateSessionHistoryForTab()
- // ==============================================================
- deleteAllNodesForTab: function(tabID)
- {
- var node;
- var ndx = 0;
-
- // Loop down cNodeList and delete all nodes with passed tabID
- // *** NOTE - cNodeList.length CHANGES when a HistoryNode is spliced
- while (historyTree.cNodeList.length > 0 && ndx < historyTree.cNodeList.length)
- {
- node = historyTree.cNodeList[ndx];
- if (node.tab === tabID)
- historyTree.cNodeList.splice(ndx, 1);
- else
- ndx ++;
- }
- },
-
- // ===========================================================
- // Returns sessHist for tabID, or null if sessHist not found
- // ===========================================================
- sessHistFromTabID: function(tabID)
- {
- // Return sessionHistory for passed tabID if its found
- for (var browNdx = 0; browNdx < gBrowser.browsers.length; browNdx++)
- {
- if (tabID === gBrowser.mTabs[browNdx].linkedPanel)
- return gBrowser.getBrowserAtIndex(browNdx).sessionHistory;
- }
-
- // Tab sessionHistory was not found
- return null;
- },
-
- // ===============================================================
- // Returns current length of cNodeList[] sessHist chain for tabID
- // ===============================================================
- sessHistLengthForTab: function(tabID)
- {
- var node;
- var lastNdx = historyTree.cNodeList.length - 1;
-
- // -------------------------------------------
- for (var ndx = lastNdx; ndx >= 0; ndx--)
- {
- node = historyTree.cNodeList[ndx];
- if (node.tab === tabID)
- return node.sessHistNdx;
- }
-
- // No matching HistoryNode was found
- return -1;
- },
-
- // =========================================================
- // Returns matching cNodeList[] entry or null if no match
- // =========================================================
- nodeFromSessHistNdx: function(sessHistNdx, tabID)
- {
- var node;
- var lastNdx = historyTree.cNodeList.length - 1;
-
- // -------------------------------------------
- for (var ndx = lastNdx; ndx >= 0; ndx--)
- {
- node = historyTree.cNodeList[ndx];
- if (node.tab === tabID && node.sessHistNdx === sessHistNdx)
- return node;
- }
-
- // No matching HistoryNode was found
- return null;
- },
-
- // =======================================================
- // Returns web-page description plus any URI "#" suffix
- // =======================================================
- pageDescFor: function(desc, URI)
- {
- // Add "#" suffix to page description if URL contains "#"
- var pos = URI.indexOf("#");
- if (pos !== -1)
- return desc + " # " + URI.substr(pos + 1);
- else
- return desc;
- },
-
- // ***************************************************************
- // ***** *****
- // ***** FUNCTIONS CALLED FROM "historyViewer.js" *****
- // ***** WHICH IS TOP LEVEL JS FOR THE EXTENSION WINDOW *****
- // ***** *****
- // ***************************************************************
-
- // ===============================================================
- // Open/Goto history page - ONLY called from "historyViewer.js"
- // *** NOTE - optNum is passed via "openPageDialog.js" onUnload()
- // ===============================================================
- openOrGotoHistoryPage: function(tNode, optNum, selTabID)
- {
- var tabNdx, sessHistNdx; // Integers
- var sessHist; // FF sessHist
-
- // ---------------------------------------------------
- // Open/Goto history page, depending on passed optNum
- if (optNum === 0)
- {
- // Get index of Tab containing required page
- tabNdx = historyTree.tabNdxFromTabID(tNode.histNode.tab);
- if (tabNdx !== -1)
- {
- // Select the Tab that contains req page
- gBrowser.mTabContainer.selectedIndex = tabNdx;
-
- // Goto req history page within Tab
- sessHistNdx = tNode.histNode.sessHistNdx;
- sessHist = gBrowser.sessionHistory;
- if (sessHist.count > sessHistNdx)
- gBrowser.gotoIndex(sessHistNdx);
- }
- }
- else if (optNum === 1)
- {
- // Add page to current Tab UNLESS it already shows that page
- openUILink(tNode.histNode.uri);
- }
- else if (optNum === 2)
- {
- // Add a new Tab with a page with passed URL in it
- gBrowser.addTab(tNode.histNode.uri);
-
- // Select the added tab
- gBrowser.mTabContainer.selectedIndex = gBrowser.browsers.length - 1;
- }
- else if (optNum === 3)
- {
- // Get index of Tab in which the page is to be opened
- tabNdx = historyTree.tabNdxFromTabID(selTabID);
- if (tabNdx !== -1)
- {
- // Select the Tab to add the page to
- gBrowser.mTabContainer.selectedIndex = tabNdx;
-
- // Add page to selected Tab UNLESS it already shows that page
- openUILink(tNode.histNode.uri);
- }
- }
- },
-
- // ======================================================
- // Returns mTabContainer.selectedIndex for passed tabID
- // ONLY called from historyTree.openOrGotoHistoryPage()
- // ======================================================
- tabNdxFromTabID: function(tabID)
- {
- // Search for Tab/Browser that has passed tabID
- for (var browNdx = 0; browNdx < gBrowser.browsers.length; browNdx++)
- {
- if (tabID === gBrowser.mTabs[browNdx].linkedPanel)
- return browNdx;
- }
-
- // Tab/Browser not found - So return -1
- return -1;
- },
-
- // ============================================================
- // Returns true if all Tabs content is loaded, or false if not
- // ============================================================
- allTabsLoaded: function()
- {
- // Return true if FF error, so any setInterval() terminates
- // Perhaps caused by "Reload All Chrome" or other...?
- if (gBrowser === null)
- return true;
-
- // Return true if all Tabs have finished loading
- var brow;
- for (var i = 0; i < gBrowser.browsers.length; i++)
- {
- brow = gBrowser.getBrowserAtIndex(i);
- if (brow.webProgress.isLoadingDocument)
- return false;
- }
- return true;
- },
-
- // ======================================================
- // Puts data needed by the extension's main window in
- // Application storage - Called from "historyViewer.js"
- // ======================================================
- putHistoryDataInAppStorage: function()
- {
- var tabID; // FF Tab identifier
- var openTabIDs = new Array();
-
- // --------------------------------------------------
- // Process all Tabs before opening extension window
- for (var i = 0; i < gBrowser.browsers.length; i++)
- {
- // Add to array list of currently open tabID's
- tabID = gBrowser.mTabs[i].linkedPanel;
- openTabIDs.push(tabID);
-
- // Update cNodeList[] sessHist entries for this Tab
- historyTree.updateSessionHistoryForTab(tabID);
- historyTree.synchSessHistChainForTab(tabID);
- }
-
- // ----------------------------------------------------------
- // Store 3 arrays in FF Application area (FF 3+ only)
- // This allow their retrieval in the extension main window
- historyTree.putWinParamsArrayInAppStorage();
- Application.storage.set("nodeArray", historyTree.cNodeList);
- Application.storage.set("tabIDarray", openTabIDs);
- },
-
- // ====================================================
- // Puts params needed by the extension in App storage
- // ====================================================
- putWinParamsArrayInAppStorage: function()
- {
- // Put data in the params array (passed into extension window)
- var winParams = new Array();
- var tabID = gBrowser.tabContainer.selectedItem.linkedPanel;
- var sessHist = historyTree.sessHistFromTabID(tabID);
- var node = historyTree.nodeFromSessHistNdx(sessHist.index, tabID);
-
- winParams.push(tabID); // tabID for visible Tab
- winParams.push(node); // HistoryNode for visible page
-
- // Put the array in App storage
- Application.storage.set("paramArray", winParams);
- },
-
- // ========================================================
- // Returns a list of all open Tabs currently open in FF
- // *** ONLY called from "historyViewer.js" onFocus() event
- // ========================================================
- openTabIDsList: function()
- {
- var openTabIDs = new Array();
- var tabID;
-
- // --------------------------------------------------
- // Process all Tabs before opening extension window
- for (var i = 0; i < gBrowser.browsers.length; i++)
- {
- // Add to array list of currently open tabID's
- tabID = gBrowser.mTabs[i].linkedPanel;
- openTabIDs.push(tabID);
- }
-
- // Return array of open TabIDs
- return openTabIDs;
- },
-
- // =============================================================
- // Returns true if any tab sessHist chain has been shortened
- // Detects history deletions via "Tools > Clear Recent History"
- // *** ONLY called from "historyViewer.js" onFocus()event
- // =============================================================
- recentFFhistoryCleared: function()
- {
- var sessHist;
- var tabID;
- var myLastNdx, lastFFndx;
-
- // Check all open tabs
- for (var browNdx = 0; browNdx < gBrowser.browsers.length; browNdx++)
- {
- // Get my last sessHistNdx
- tabID = gBrowser.mTabs[browNdx].linkedPanel;
- myLastNdx = historyTree.sessHistLengthForTab(tabID);
-
- // Get FF last sessHistNdx
- sessHist = gBrowser.getBrowserAtIndex(browNdx).sessionHistory;
- lastFFndx = sessHist.count - 1;
-
- // Check if this tabs chain has been shortened
- if (myLastNdx > lastFFndx)
- return true;
- }
-
- // No tab sessHist chain has been shortened
- return false;
- },
-
- };
- // End of extension overlay and HistoryTree.prototype